#pragma comment( linker, "/defaultlib:ws2_32.lib" )

#include "client.h"
#include <algorithm>

using namespace std;

bool client::fileExist(const string &fileName)
{
	ifstream file(fileName);
	if (file.good())
	{
		file.close();
		return true;
	}
	else
	{
		file.close();
		return false;
	}
}

int client::getFileSize(const string &fileName)
{
	ifstream file(fileName, ifstream::binary);
	file.seekg(0, file.end);
	int size = file.tellg();
	file.close();
	return size;
}


int client::getTextFileCount(const string &fileName)
{
	ifstream file(fileName);
	int count = 0;
	while (true)
	{
		int position = 0;
		while (!file.eof())
		{
			char c = file.get();
			if (c != EOF)
			{
				position++;
				if (position == DATA_SIZE - 1)
				{
					break;
				}
			}
		}
		count++;
		if (file.eof())
		{
			break;
		}
	}
	file.close();
	return count;
}

string client::getSystemUserName()
{
	char username[UNLEN + 1];
	DWORD username_len = UNLEN + 1;
	GetUserName(username, &username_len);
	//std::cout << username;
	string strUserName(username);
	return strUserName;
}

string client::getLocalhostName()
{
	#define MAXHOSTNAMELEN 256			//maximum length of host name
	char localhost[MAXHOSTNAMELEN];
	gethostname(localhost, MAXHOSTNAMELEN);
	return localhost;
}

bool client::isBinaryFile(const string &fileName)
{
	int c;
	ifstream file(fileName);
	while ((c = file.get()) != EOF && c <= 127)
		;
	if (c == EOF)
	{
		file.close();
		return false;
	}
	else
	{
		file.close();
		return true;
	}
}

int client::getNextSequenceNumber(int sequenceNumber)
{
	return sequenceNumber + 1;
}


int client::stringToInt(string s)
{
	istringstream stringBuffer(s);
	int value;
	stringBuffer >> value;
	return value;
}

client::client(char *fn)
{
	hostent *hp;
	sockaddr_in clientaddr;
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(SERVER_PORT);

	WSADATA wsadata;
	if (WSAStartup(0x0202, &wsadata) != 0)
	{
		std::cout << "error in starting WSAStartup()" << endl;
		std::exit(1);
	}

	char remotehost[32];
	std::cout << "please enter remote server name:";
	cin >> remotehost;

	hp = gethostbyname(remotehost);
	if (hp == NULL){
		std::cout << "can not find remote server" << endl;
		std::exit(1);
	}

	if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET)
	{
		std::cout << "can not create socket" << endl;
		std::exit(1);
	}
	memcpy((void *)&servaddr.sin_addr, hp->h_addr_list[0], hp->h_length);

	clientaddr.sin_family = AF_INET;
	clientaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	clientaddr.sin_port = htons(CLIENT_PORT);

	//Bind the server port
	if (bind(fd, (struct sockaddr *)&clientaddr, sizeof(clientaddr)) < 0)
	{
		std::cout << "can not bind socket" << endl;
		std::exit(1);
	}

	fout.open(fn);
	run();
}
client::~client()
{
	closesocket(fd);
	WSACleanup();
	fout.close();
}

void client::run()
{
	struct timeval timeouts;
	timeouts.tv_sec = 0;
	timeouts.tv_usec = 300000;

	while (true)
	{
		std::cout << "Input operation(get, put, list, rename), input \"quit\" to leave:";
		string operation;
		cin >> operation;

		//quit program
		if (operation == "quit")
			break;

		//check operation validity
		if (operation != "get" && operation != "put" && operation != "list" && operation != "quit" && operation !="rename")
		{
			std::cout << "Your input is wrong!" << endl;
			continue;
		}

		string fileName;
		if (operation == "get" || operation == "put" || operation == "rename" )
		{
			std::cout << "Input file name:";
			cin >> fileName;
		}

		string fileNameforGet = fileName;
		//check if file exists
		if (operation == "get")
		{
			if (fileExist(fileName))
			{
				cout << "file already exists." << endl
					<< "input \"c\" to cancel or \"r\" to rename file:";
				string cmd;
				cin >> cmd;
				if (cmd == "c")
				{
					cout << "get operation cancelled." << endl;
					continue;
				}
				else if (cmd == "r")
				{
					cout << "input new file name:";
					cin >> fileNameforGet;
				}
			}
		}

		string fileNameforPut = fileName;

		string newName;
		if (operation == "rename")
		{
			cout << "please input new file name:";
			cin >> newName;
			cout << "Are you sure to rename " + fileName + " to " + newName + "?" << endl
				<< "input \"y\" to confirm, input \"c\" to cancel:";
			string cmd;
			cin >> cmd;
			if (cmd=="c")
			{
				cout << "rename cancelled" << endl;
				continue;
			}
			else if (cmd !="y")
			{
				cout << "invalid input. input again" << endl;
				continue;
			}
		}

		bool handshakeSucceed = false;
		int initialSequenceNumber;

		//three way hand shake
		//srand(time(NULL));
		int random = rand() % 256;
		std::cout << "random generated:" << random << endl;
		myPacket *mp;
		mp = (myPacket*)malloc(sizeof(myPacket));
		mp->sequenceNumber = -100;
		strcpy_s(mp->data, std::to_string(random).c_str());
		std::cout << "first handshake packet to send:" << mp->data << endl;
		char * buf = (char*)mp;
		//send first handshake packet
		sendto(fd, buf, sizeof(myPacket), 0, (SOCKADDR *)&servaddr, sizeof(servaddr));
		if (TRACE)
		{
			fout << "Sender: handshake packet 1 sent "  <<mp->data<< endl;
			fout.flush();
		}
		int receivedNumber1;
		int receivedNumber2;
		while (true)
		{
			fd_set readfds;
			FD_ZERO(&readfds);
			FD_SET(fd, &readfds);
			int outfds;
			//timeout, resend
			if (!(outfds = select(1, &readfds, NULL, NULL, &timeouts)))
			{
				std::cout << "first handshake timeout, resend" << endl;
				sendto(fd, buf, sizeof(myPacket), 0, (SOCKADDR *)&servaddr, sizeof(servaddr));
				if (TRACE)
				{
					fout << "Sender: handshake packet 1 timeout, resent "  <<mp->data<< endl;
					fout.flush();
				}
			}
			//not timeout
			if (outfds == 1)
			{
				char buffer[sizeof(myPacket)];
				int length = sizeof(servaddr);
				//receive second handshake packet
				recvfrom(fd, buffer, sizeof(myPacket), 0, (struct sockaddr *)&servaddr, &length);
				myPacket *mp2;
				mp2 = (myPacket*)malloc(sizeof(myPacket));
				mp2 = (myPacket*)buffer;
				if (mp2->sequenceNumber == mp->sequenceNumber && mp2->acknowledgement)
				{
					std::cout << "second handshake packet received:" << mp2->data << endl;
					if (TRACE)
					{
						fout << "Sender: handshake packet 2 received "  <<mp2->data<< endl;
						fout.flush();
					}
					string numbers(mp2->data);
					int index = numbers.find(",");
					receivedNumber1 = stringToInt(numbers.substr(0, index));
					receivedNumber2 = stringToInt(numbers.substr(index + 1));
					break;
				}
				else
				{
					sendto(fd, buf, sizeof(myPacket), 0, (SOCKADDR *)&servaddr, sizeof(servaddr));
					if (TRACE)
					{
						fout << "Sender: handshake packet 1 resent " << mp->data << endl;
						fout.flush();
					}
				}
			}
		}

		mp = (myPacket*)malloc(sizeof(myPacket));
		mp->sequenceNumber = -101;
		strcpy_s(mp->data, std::to_string(receivedNumber2).c_str());
		std::cout << "third handshake packet to send:" << mp->data << endl;
		buf = (char*)mp;
		//send third handshake packet
		sendto(fd, buf, sizeof(myPacket), 0, (SOCKADDR *)&servaddr, sizeof(servaddr));
		if (TRACE)
		{
			fout << "Sender: handshake packet 3 sent "  <<mp->data<< endl;
			fout.flush();
		}
		while (true)
		{
			fd_set readfds;
			FD_ZERO(&readfds);
			FD_SET(fd, &readfds);
			int outfds;
			//timeout, resend
			if (!(outfds = select(1, &readfds, NULL, NULL, &timeouts)))
			{
				std::cout << "third handshake timeout, resend" << endl;
				sendto(fd, buf, sizeof(myPacket), 0, (SOCKADDR *)&servaddr, sizeof(servaddr));
				if (TRACE)
				{
					fout << "Sender: handshake packet 3 timeout, resent "  <<mp->data<< endl;
					fout.flush();
				}
			}
			//not timeout
			if (outfds == 1)
			{
				char buffer[sizeof(myPacket)];
				int length = sizeof(servaddr);
				//receive second handshake packet
				recvfrom(fd, buffer, sizeof(myPacket), 0, (struct sockaddr *)&servaddr, &length);
				myPacket *mp2;
				mp2 = (myPacket*)malloc(sizeof(myPacket));
				mp2 = (myPacket*)buffer;
				if (mp2->sequenceNumber == mp->sequenceNumber && mp2->acknowledgement)
				{
					//receive ACK of third handshake packet
					break;
				}
				else
				{
					sendto(fd, buf, sizeof(myPacket), 0, (SOCKADDR *)&servaddr, sizeof(servaddr));
					if (TRACE)
					{
						fout << "Sender: handshake packet 3 resent " << mp->data << endl;
						fout.flush();
					}
				}
			}
		}

		if (receivedNumber1 == random)
		{
			handshakeSucceed = true;
			std::cout << "handshake succeeded" << endl;
			if (TRACE)
			{
				fout << "Sender: handshake succeeded "  << endl;
				fout.flush();
			}
			initialSequenceNumber = receivedNumber2;
			std::cout << "initial sequence number:" << initialSequenceNumber << endl;
		}

		if (handshakeSucceed)
		{
			string userName = getSystemUserName();
			string localhost = getLocalhostName();
			if (operation == "put")
			{
				if (TRACE)
				{
					fout << "Sender: starting on host " << localhost << endl;
					fout.flush();
				}
				if (fileExist(fileName))
				{
					bool binaryFile = isBinaryFile(fileName);
					int binaryFlag = binaryFile ? 1 : 0;
					ifstream fin;
					int count;
					if (binaryFile)
					{
						int fileSize = getFileSize(fileName);
						//std::cout << "binary file size:" << fileSize << endl;
						count = fileSize % (DATA_SIZE - 1) == 0 ? fileSize / (DATA_SIZE - 1) : fileSize / (DATA_SIZE - 1) + 1;
						//std::cout << "binary file count:" << count << endl;
						fin = ifstream(fileName, ifstream::binary);
					}
					else
					{
						count = getTextFileCount(fileName);
						//std::cout << "text file count:" << count << endl;
						fin = ifstream(fileName);
					}

					bool cancelFlag = false;
					while (true)
					{
						string text = "operation:put|userName:" + userName + "|fileName:" + fileNameforPut + "|count:" + to_string(count)
							+ "|binary:" + to_string(binaryFlag);
						//std::cout << "text:" << text << endl;
						//send control packet
						myPacket *mp;
						mp = (myPacket*)malloc(sizeof(myPacket));
						mp->sequenceNumber = initialSequenceNumber;
						mp->terminate = false;
						mp->duplicate = false;
						strcpy_s(mp->data, text.c_str());
						buf = (char*)mp;
						sendto(fd, buf, sizeof(myPacket), 0, (SOCKADDR *)&servaddr, sizeof(servaddr));
						if (TRACE)
						{
							fout << "Sender: sent packet " << mp->sequenceNumber << endl;
							fout.flush();
						}
						bool continueFlag = false;
						while (true)
						{
							fd_set readfds;
							FD_ZERO(&readfds);
							FD_SET(fd, &readfds);
							int outfds;
							//timeout, resend
							if (!(outfds = select(1, &readfds, NULL, NULL, &timeouts)))
							{
								sendto(fd, buf, sizeof(myPacket), 0, (SOCKADDR *)&servaddr, sizeof(servaddr));
								if (TRACE)
								{
									fout << "Sender: timeout resend packet " << mp->sequenceNumber << endl;
									fout.flush();
								}
							}
							//not timeout
							if (outfds == 1)
							{
								char buffer[sizeof(myPacket)];
								int length = sizeof(servaddr);
								recvfrom(fd, buffer, sizeof(myPacket), 0, (struct sockaddr *)&servaddr, &length);
								myPacket *mp2;
								mp2 = (myPacket*)malloc(sizeof(myPacket));
								mp2 = (myPacket*)buffer;
								if (mp2->sequenceNumber == mp->sequenceNumber && mp2->acknowledgement && mp2->duplicate)
								{
									cout << "File with the same name already exists on server." << endl
										<< "input \"c\" to cancel or \"r\" to rename:";
									string cmd;
									cin >> cmd;
									transform(cmd.begin(), cmd.end(), cmd.begin(), ::tolower);
									if (cmd == "c")
									{
										cancelFlag = true;
										break;
									}
									else if (cmd == "r")
									{
										cout << "input new name of the file:";
										cin >> fileNameforPut;
										//cout << "fileNameforPut:" + fileNameforPut << endl;
										continueFlag = true;
										break;
									}
								}
								//check sequence number and acknowledgement
								else if (mp2->sequenceNumber == mp->sequenceNumber && mp2->acknowledgement && (!mp2->duplicate))
								{
									if (TRACE)
									{
										fout << "Sender: received ACK for packet " << mp->sequenceNumber << endl;
										fout.flush();
									}
									break;
								}
								else
								{
									//resend
									sendto(fd, buf, sizeof(myPacket), 0, (SOCKADDR *)&servaddr, sizeof(servaddr));
									if (TRACE)
									{
										fout << "Sender: resend packet " << mp->sequenceNumber << endl;
										fout.flush();
									}
								}
							}
						}
						if (!continueFlag)
						{
							break;
						}
					}

					if (cancelFlag)
					{
						cout << "put operation cancelled." << endl;
						continue;
					}

					cout << "count:" << count << endl;
					//send data packets
					std::cout << "begin to send file" << endl;
					int effectivePacketNum = 0;
					int packetNum = 0;
					int sequenceNumber = initialSequenceNumber+1;
					myPacket * arrayPoint = new myPacket[count];
					//binary file
					if (binaryFile)
					{
						//read file into an array
						for (int i = 0; i < count; i++)
						{
							char array[DATA_SIZE]; // allocating an array
							memset(array, '\0', DATA_SIZE);
							fin.read(array, DATA_SIZE - 1);
							int readSize = fin.gcount();
							myPacket * mp = arrayPoint + i;
							mp->sequenceNumber = sequenceNumber++;
							mp->terminate = false;
							mp->dataSize = readSize;
							memcpy(mp->data, array, readSize + 1);
						}
					}
					else
					{
						//read file into an array
						for (int i = 0; i < count; i++)
						{
							char array[DATA_SIZE]; // allocating an array
							memset(array, '\0', DATA_SIZE);
							int position = 0; //this will be used incremently to fill characters in the array 
							//this loop run until end of file (eof) does not occur
							while (!fin.eof())
							{
								char c = fin.get(); //reading one character from file to array
								if (c != EOF)
								{
									array[position] = c;
									position++;
									if (position == DATA_SIZE - 1)	//full
									{
										array[position] = '\0';
										break;
									}
								}
							}
							if (position != DATA_SIZE - 1)
							{
								array[position] = '\0';
							}
							myPacket * mp = arrayPoint + i;
							mp->sequenceNumber = sequenceNumber++;
							mp->terminate = false;
							mp->dataSize = strlen(array);
							strcpy_s(mp->data, strlen(array) + 1, array);
						}
					}
					//close file
					fin.close();

					//assign variables
					int base = initialSequenceNumber + 1;
					int nextSeq = initialSequenceNumber + 1;

					//send first WINDOW_SIZE packets
					for (int i = 0; i < WINDOW_SIZE; i++)
					{
						buf = (char*)(arrayPoint+i);
						//cout << "send sequence number:" << (arrayPoint + i)->sequenceNumber << endl;
						sendto(fd, buf, sizeof(myPacket), 0, (SOCKADDR *)&servaddr, sizeof(servaddr));
						if (TRACE)
						{
							fout << "Sender: send packet " << (arrayPoint + i)->sequenceNumber << endl;
							fout.flush();
							effectivePacketNum++;
							packetNum++;
						}
					}
					nextSeq += WINDOW_SIZE;

					//timer
					while (true)
					{
						fd_set readfds;
						FD_ZERO(&readfds);
						FD_SET(fd, &readfds);
						int outfds;
						//cout << "base:" << base << endl;
						//cout << "nextSeq:" << nextSeq << endl;
						//timeout, resend
						if (!(outfds = select(1, &readfds, NULL, NULL, &timeouts)))
						{
							//cout << "timeout" << endl;
							//resend
							for (int i = base-(initialSequenceNumber+1); i < nextSeq-(initialSequenceNumber+1); i++)
							{
								buf = (char*)(arrayPoint + i);
								sendto(fd, buf, sizeof(myPacket), 0, (SOCKADDR *)&servaddr, sizeof(servaddr));
								//cout << "resend sequence number:" << (arrayPoint + i)->sequenceNumber << endl;
								if (TRACE)
								{
									fout << "Sender: timeout, resend packet " << (arrayPoint + i)->sequenceNumber << endl;
									fout.flush();
									packetNum++;
								}
							}
						}
						//not timeout
						if (outfds == 1)
						{
							//cout << "not timeout" << endl;
							char buffer[sizeof(myPacket)];
							int length = sizeof(servaddr);
							recvfrom(fd, buffer, sizeof(myPacket), 0, (struct sockaddr *)&servaddr, &length);

							myPacket *mp2;
							mp2 = (myPacket*)malloc(sizeof(myPacket));
							mp2 = (myPacket*)buffer;
							//check sequence number and acknowledgement
							if (mp2->acknowledgement)
							{
								if (TRACE)
								{
									fout << "Sender: received ACK for packet " << mp2->sequenceNumber << endl;
									fout.flush();
								}
								int ackSeqNum = mp2->sequenceNumber;
								//cout << "ACK sequence number:" << ackSeqNum << endl;
								//update base
								if (ackSeqNum >= base)
								{
									base = ackSeqNum + 1;
									//cout << "base increment:" << base << endl;
								}
								else
								{
									continue;
								}
								//send subsequent packets
								if (nextSeq<initialSequenceNumber+1+count && base+WINDOW_SIZE>nextSeq)
								{
									int upperBound = min(base + WINDOW_SIZE, initialSequenceNumber + 1 + count);
									//cout << "upperBound:" << upperBound << endl;
									for (int i = nextSeq-(initialSequenceNumber + 1); i < upperBound-(initialSequenceNumber + 1); i++)
									{
										buf = (char*)(arrayPoint + i);
										//cout << "send sequence number:" << (arrayPoint + i)->sequenceNumber << endl;
										sendto(fd, buf, sizeof(myPacket), 0, (SOCKADDR *)&servaddr, sizeof(servaddr));
										if (TRACE)
										{
											fout << "Sender: send packet " << (arrayPoint + i)->sequenceNumber << endl;
											fout.flush();
											effectivePacketNum++;
											packetNum++;
										}
									}
									nextSeq = upperBound;
								}
								//receive ACK for last packet
								else if (ackSeqNum == initialSequenceNumber + count)
								{
									cout << "receive ACK for last seq num" << endl;
									break;
								}
							}
							else
							{
								//cout << "NAK sequence number:" << mp2->sequenceNumber << endl;
							}
						}
					}

					//send terminate packet 10 times
					mp = (myPacket*)malloc(sizeof(myPacket));
					mp->sequenceNumber = sequenceNumber++;
					mp->terminate = true;
					char * buf = (char*)mp;
					for (int i = 0; i < 10; i++)
					{
						//cout << "sequence number:" << mp->sequenceNumber << endl;
						sendto(fd, buf, sizeof(myPacket), 0, (SOCKADDR *)&servaddr, sizeof(servaddr));
					}
					
					delete[] arrayPoint;

					std::cout << "file completely transfered!" << endl;
					if (TRACE)
					{
						fout << "Sender: file transfer completed " << endl
							<< "Sender: number of effective packets sent: " << effectivePacketNum << endl
							<< "Sender: number of effective bytes sent:" << effectivePacketNum*sizeof(myPacket) << endl
							<< "Sender: number of packets sent: " << packetNum << endl
							<< "Sender: number of bytes sent: " << packetNum*sizeof(myPacket) << endl;
						fout.flush();
					}
				}
				else
				{
					std::cout << "File could not be opened." << endl;
				}
			}
			else if (operation == "get")
			{
				if (TRACE)
				{
					fout << "Sender: starting on host " << localhost << endl;
					fout.flush();
				}
				//send request
				myPacket *mp;
				mp = (myPacket*)malloc(sizeof(myPacket));
				mp->sequenceNumber = initialSequenceNumber;
				mp->terminate = false;
				mp->acknowledgement = false;
				string text = "operation:get|userName:" + userName + "|fileName:" + fileName;
				//std::cout << "text:" << text << endl;
				strcpy_s(mp->data, text.c_str());//put request msg in the packet
				buf = (char*)mp;
				if (TRACE)
				{
					fout << "Sender: send packet " << mp->sequenceNumber << endl;
					fout.flush();
				}
				sendto(fd, buf, sizeof(myPacket), 0, (SOCKADDR *)&servaddr, sizeof(servaddr));
				int count;
				bool isBinaryFile;
				bool hasError = false;
				while (true)
				{
					fd_set readfds;
					FD_ZERO(&readfds);
					FD_SET(fd, &readfds);
					int outfds;
					//timeout, resend
					if (!(outfds = select(1, &readfds, NULL, NULL, &timeouts)))
					{
						if (TRACE)
						{
							fout << "Sender: timeout resent packet " << mp->sequenceNumber << endl;
							fout.flush();
						}
						sendto(fd, buf, sizeof(myPacket), 0, (SOCKADDR *)&servaddr, sizeof(servaddr));
					}
					//not timeout
					if (outfds == 1)
					{
						char buffer[sizeof(myPacket)];
						int length = sizeof(servaddr);
						recvfrom(fd, buffer, sizeof(myPacket), 0, (struct sockaddr *)&servaddr, &length);//get response from server
						myPacket *mp2;
						mp2 = (myPacket*)malloc(sizeof(myPacket));
						mp2 = (myPacket*)buffer;
						//check sequence number and acknowledgement
						if (mp2->sequenceNumber == mp->sequenceNumber && mp2->acknowledgement)
						{
							if (TRACE)
							{
								fout << "Sender: received ACK for packet " << mp->sequenceNumber << endl;
								fout.flush();
							}
							string result = mp2->data;
							if (result.find("count") == 0)
							{
								count = stringToInt(result.substr(6));
								int index = result.find_first_of("|");
								int binaryFlag = stringToInt(result.substr(index + 8));
								isBinaryFile = binaryFlag == 1 ? true : false;
								break;
							}
							else
							{
								cout << mp2->data << endl;
								hasError = true;
								break;
							}
						}
						else
						{
							//resend request
							sendto(fd, buf, sizeof(myPacket), 0, (SOCKADDR *)&servaddr, sizeof(servaddr));
							if (TRACE)
							{
								fout << "Sender: resend packet " << mp->sequenceNumber << endl;
								fout.flush();
							}
						}
					}
				}
				if (hasError)
				{
					continue;
				}

				//create file
				ofstream ofs;
				if (isBinaryFile)
				{
					ofs = ofstream(fileNameforGet, ofstream::binary);
				}
				else
				{
					ofs = ofstream(fileNameforGet, ofstream::out);
				}
				//get file data
				int rcvNum=0;
				cout << "receive file..." << endl;
				int expectedSequenceNumber = random;
				while (true)
				{
					char buffer[sizeof(myPacket)];
					int length = sizeof(servaddr);
					recvfrom(fd, buffer, sizeof(myPacket), 0, (struct sockaddr *)&servaddr, &length);

					myPacket *mp;
					mp = (myPacket*)malloc(sizeof(myPacket));
					mp = (myPacket*)buffer;
					if (TRACE)
					{
						fout << "Receiver: received packet " << mp->sequenceNumber << endl;
						fout.flush();
						rcvNum++;
					}
					//terminate packet
					if (mp->sequenceNumber==expectedSequenceNumber && mp->terminate)
					{
						//cout << "terminate packet" << endl;
						break;
					}
					else if (mp->sequenceNumber<expectedSequenceNumber && (!mp->terminate))
					{
						myPacket *mp2;
						mp2 = (myPacket*)malloc(sizeof(myPacket));
						mp2->sequenceNumber = mp->sequenceNumber;
						mp2->acknowledgement = true;
						char * replyBuf = (char*)mp2;
						if (TRACE)
						{
							fout << "Receiver: send an ACK for packet " << mp->sequenceNumber << endl;
							fout.flush();
						}
						sendto(fd, replyBuf, sizeof(myPacket), 0, (SOCKADDR *)&servaddr, sizeof(servaddr));
					}
					//correct packet
					else if (mp->sequenceNumber==expectedSequenceNumber && (!mp->terminate))
					{
						//cout << "correct sequence number:" << mp->sequenceNumber << endl;
						//response
						myPacket *mp2;
						mp2 = (myPacket*)malloc(sizeof(myPacket));
						mp2->sequenceNumber = mp->sequenceNumber;
						mp2->acknowledgement = true;
						char * replyBuf = (char*)mp2;
						if (TRACE)
						{
							fout << "Receiver: send an ACK for packet " << mp->sequenceNumber << endl;
							fout.flush();
						}
						sendto(fd, replyBuf, sizeof(myPacket), 0, (SOCKADDR *)&servaddr, sizeof(servaddr));
						expectedSequenceNumber++;
						char szbuffer[DATA_SIZE];
						memset(szbuffer, '\0', DATA_SIZE);
						if (isBinaryFile)
						{
							std::memcpy(szbuffer, mp->data, mp->dataSize);
							ofs.write(szbuffer, mp->dataSize);
						}
						else
						{
							strcpy_s(szbuffer, mp->dataSize + 1, mp->data);
							ofs << szbuffer;
						}
						ofs.flush();
					}
					//wrong packet
					else
					{
						//cout << "wrong seq, expected seq:" << expectedSequenceNumber << endl;
						//response
						myPacket *mp2;
						mp2 = (myPacket*)malloc(sizeof(myPacket));
						mp2->sequenceNumber = mp->sequenceNumber;
						mp2->acknowledgement = false;
						char * replyBuf = (char*)mp2;
						if (TRACE)
						{
							fout << "Receiver: send an ACK for packet " << mp->sequenceNumber << endl;
							fout.flush();
						}
						sendto(fd, replyBuf, sizeof(myPacket), 0, (SOCKADDR *)&servaddr, sizeof(servaddr));
					}
				}
				ofs.close();
				cout << "file completely received" << endl;
				if(TRACE)
				{
					fout << "Receiver: transfer completed " << endl
						<<"Receiver: number of bytes received:"<<rcvNum*sizeof(myPacket)<<endl;
					fout.flush();
				}
			}
			else if (operation == "list")
			{
				string text = "operation:list|userName:" + userName;
				//send control packet
				myPacket *mp;
				mp = (myPacket*)malloc(sizeof(myPacket));
				mp->sequenceNumber = initialSequenceNumber;
				mp->acknowledgement = false;
				strcpy_s(mp->data, text.c_str());
				buf = (char*)mp;
				int effectivePacketNum = 0;
				int packetNum = 0;
				if (TRACE)
				{
					fout << "Sender: send packet " << mp->sequenceNumber << endl;
					fout.flush();
					effectivePacketNum++;
					packetNum++;
				}
				sendto(fd, buf, sizeof(myPacket), 0, (SOCKADDR *)&servaddr, sizeof(servaddr));
				int count;
				while (true)
				{
					fd_set readfds;
					FD_ZERO(&readfds);
					FD_SET(fd, &readfds);
					int outfds;
					//timeout, resend
					if (!(outfds = select(1, &readfds, NULL, NULL, &timeouts)))
					{
						sendto(fd, buf, sizeof(myPacket), 0, (SOCKADDR *)&servaddr, sizeof(servaddr));
						if (TRACE)
						{
							fout << "Sender: timeout resend packet " << mp->sequenceNumber << endl;
							fout.flush();
							packetNum++;
						}
					}
					//not timeout
					if (outfds == 1)
					{
						char buffer[sizeof(myPacket)];
						int length = sizeof(servaddr);
						recvfrom(fd, buffer, sizeof(myPacket), 0, (struct sockaddr *)&servaddr, &length);
						myPacket *mp2;
						mp2 = (myPacket*)malloc(sizeof(myPacket));
						mp2 = (myPacket*)buffer;
						//check sequence number and acknowledgement
						if (mp2->sequenceNumber == mp->sequenceNumber && mp2->acknowledgement)
						{
							//std::cout << "data:  " << mp2->data << endl;
							count = stringToInt(string(mp2->data).substr(6));
							if (TRACE)
							{
								fout << "Sender: received ACK for packet " << mp->sequenceNumber << endl;
								fout.flush();
							}
							break;
						}
						else
						{
							//resend
							sendto(fd, buf, sizeof(myPacket), 0, (SOCKADDR *)&servaddr, sizeof(servaddr));
							if (TRACE)
							{
								fout << "Sender: resend packet " << mp->sequenceNumber << endl;
								fout.flush();
								packetNum++;
							}
						}
					}
				}
				if (TRACE)
				{
					fout << "Sender: number of effective bytes sent:" << effectivePacketNum*sizeof(myPacket) << endl
						<< "Sender: number of packets sent: " << packetNum << endl
						<< "Sender: number of bytes sent: " << packetNum*sizeof(myPacket) << endl;
					fout.flush();
				}
				//std::cout << "count:" << count << endl;
				int rcvNum = 0;

				//get file list
				string fileList;
				int expectedSequenceNumber = random;
				while (true)
				{
					//std::cout << count << endl;
					char buffer[sizeof(myPacket)];
					int length = sizeof(servaddr);
					recvfrom(fd, buffer, sizeof(myPacket), 0, (struct sockaddr *)&servaddr, &length);

					myPacket *mp;
					mp = (myPacket*)malloc(sizeof(myPacket));
					mp = (myPacket*)buffer;
					if (TRACE)
					{
						fout << "Receiver: received packet " << mp->sequenceNumber << endl;
						fout.flush();
						rcvNum++;
					}
					//terminate packet
					if (mp->sequenceNumber == expectedSequenceNumber && mp->terminate)
					{
						break;
					}
					//old packet
					else if (mp->sequenceNumber < expectedSequenceNumber && (!mp->terminate))
					{
						myPacket *mp2;
						mp2 = (myPacket*)malloc(sizeof(myPacket));
						mp2->sequenceNumber = mp->sequenceNumber;
						mp2->acknowledgement = true;
						char * replyBuf = (char*)mp2;
						sendto(fd, replyBuf, sizeof(myPacket), 0, (SOCKADDR *)&servaddr, sizeof(servaddr));
					}
					else if (mp->sequenceNumber == expectedSequenceNumber && (!mp->terminate))
					{
						myPacket *mp2;
						mp2 = (myPacket*)malloc(sizeof(myPacket));
						mp2->sequenceNumber = mp->sequenceNumber;
						mp2->acknowledgement = true;
						char * replyBuf = (char*)mp2;
						sendto(fd, replyBuf, sizeof(myPacket), 0, (SOCKADDR *)&servaddr, sizeof(servaddr));
						expectedSequenceNumber++;
						fileList += string(mp->data);
					}
					else
					{
						myPacket *mp2;
						mp2 = (myPacket*)malloc(sizeof(myPacket));
						mp2->sequenceNumber = mp->sequenceNumber;
						mp2->acknowledgement = false;
						char * replyBuf = (char*)mp2;
						sendto(fd, replyBuf, sizeof(myPacket), 0, (SOCKADDR *)&servaddr, sizeof(servaddr));
					}
				}
				//cout << "receive complete" << endl;
				cout << "files in directory in server:" << endl;
				cout << fileList << endl;
				if (TRACE)
				{
					fout << "Receiver: transfer completed " << endl
						<< "Receiver: number of bytes received:" << rcvNum*sizeof(myPacket) << endl;
					fout.flush();
				}
			}
			else if (operation == "rename")
			{
				string text = "operation:rename|userName:" + userName + "|fileName:" + fileName + "|newName:" + newName;
				//send control packet
				myPacket *mp;
				mp = (myPacket*)malloc(sizeof(myPacket));
				mp->sequenceNumber = initialSequenceNumber;
				strcpy_s(mp->data, text.c_str());
				buf = (char*)mp;
				if (TRACE)
				{
					fout << "Sender: send packet " << mp->sequenceNumber << endl;
					fout.flush();
				}
				sendto(fd, buf, sizeof(myPacket), 0, (SOCKADDR *)&servaddr, sizeof(servaddr));
				while (true)
				{
					fd_set readfds;
					FD_ZERO(&readfds);
					FD_SET(fd, &readfds);
					int outfds;
					//timeout, resend
					if (!(outfds = select(1, &readfds, NULL, NULL, &timeouts)))
					{
						sendto(fd, buf, sizeof(myPacket), 0, (SOCKADDR *)&servaddr, sizeof(servaddr));
						if (TRACE)
						{
							fout << "Sender: timeout resend packet " << mp->sequenceNumber << endl;
							fout.flush();
						}
					}
					//not timeout
					if (outfds == 1)
					{
						char buffer[sizeof(myPacket)];
						int length = sizeof(servaddr);
						recvfrom(fd, buffer, sizeof(myPacket), 0, (struct sockaddr *)&servaddr, &length);
						myPacket *mp2;
						mp2 = (myPacket*)malloc(sizeof(myPacket));
						mp2 = (myPacket*)buffer;
						//rename succeeded
						if (mp2->sequenceNumber == mp->sequenceNumber && mp2->acknowledgement && (!mp2->terminate))
						{
							cout << "rename succeeded" << endl;
						}
						//rename failed
						else if (mp2->sequenceNumber == mp->sequenceNumber && mp2->acknowledgement && mp2->terminate)
						{
							cout << "rename failed:" << mp2->data << endl;
						}
						break;
					}
				}
				cout << "rename finished" << endl;
			}
		}
		else
		{
			std::cout << "hand shake failed. Try again" << endl;
		}
	}
}



int main(void)
{
	client c;
	c.run();
}